package com.cburch.logisim.std.gates;

import java.awt.Color;
import java.awt.Graphics;

import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.AttributeOption;
import com.cburch.logisim.data.AttributeSet;
import com.cburch.logisim.data.Attributes;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstanceFactory;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.Port;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.util.IntegerFactory;
import com.cburch.logisim.util.Strings;
import com.cburch.logisim.UItools.GraphicsUtil;
import com.cburch.logisim.UItools.Point;

public class NotGate extends InstanceFactory {
	
	public static NotGate FACTORY = new NotGate();
	static final int PER_DELAY = 1;
    private static final int IN = 0;
    private static final int OUT = 1;
    
    public static final AttributeOption SIZE_NARROW
    	= new AttributeOption(IntegerFactory.create(20),
        Strings.getter("gateSizeNarrowOpt", Strings.stdSource));
    public static final AttributeOption SIZE_WIDE
    	= new AttributeOption(IntegerFactory.create(30),
        Strings.getter("gateSizeWideOpt", Strings.stdSource));
	public static final Attribute ATTR_SIZE
		= Attributes.forOption("size", Strings.getter("gateSizeAttr", Strings.stdSource),
        new AttributeOption[] { SIZE_NARROW, SIZE_WIDE });
	
	public NotGate() {
        super("NOT Gate", Strings.getter("notComponent", Strings.stdSource));
        setAttributes(new Attribute[] {
        		StdAttr.FACING,
        		StdAttr.WIDTH,
        		ATTR_SIZE,
            }, new Object[] {
                Direction.EAST,
                BitWidth.create(1),
                SIZE_WIDE,
            });        
        setIconName("notGate.gif");
        setFacingAttribute(StdAttr.FACING);
        setOffsetBounds(Bounds.create(-30, -10, 30, 20));
        
    }
	
    protected void configureNewInstance(Instance instance) {
        instance.addAttributeListener();
        updatePorts(instance);
    }
	
	public void propagate(InstanceState state) {
        // get attributes
        BitWidth dataWidth = (BitWidth) state.getAttributeValue(StdAttr.WIDTH);

        // get input
        Value a = state.getPort(IN);

        // propagate them
        int delay = (dataWidth.getWidth() + 2) * PER_DELAY;
        
        state.setPort(OUT, a.not(), delay);
    }
	
	public void paintInstance(InstancePainter painter) {
        
		Graphics g = painter.getGraphics(); 
		painter.drawPort(IN);
        painter.drawPort(OUT);
        g.setColor(Color.BLACK);
        drawNotGate(painter);        
	}
	
    public void paintGhost(InstancePainter painter) {
    	
        Graphics g = painter.getGraphics();
        g.setColor(Color.GRAY);
        drawNotGate(painter);
    }
    
    protected void instanceAttributeChanged(Instance instance, Attribute attr) {
        if(attr == StdAttr.FACING || attr == ATTR_SIZE) {
            instance.recomputeBounds();
            updatePorts(instance);
        }
    }
    
    private void updatePorts(Instance instance) {
    	
    	Port[] ps = new Port[2];
        int x1, y1;
        
        Object dir = instance.getAttributeValue(StdAttr.FACING);
        if(dir == Direction.EAST) {
            x1 = -30; y1 = 0;
        } else if(dir == Direction.WEST) {
            x1 = 30; y1 = 0;            
        } else if(dir == Direction.NORTH) {
        	x1 = 0; y1 = 30;
        } else {
        	x1 = 0; y1 = -30;           
        }
        
        ps[IN]   = new Port(x1, y1, Port.INPUT,  StdAttr.WIDTH);
        ps[OUT]   = new Port(0, 0, Port.OUTPUT, StdAttr.WIDTH);
        instance.setPorts(ps);
    }
    
    public Bounds getOffsetBounds(AttributeSet attrs) {
        Direction dir = (Direction) attrs.getValue(StdAttr.FACING);
        return Bounds.create(-30, -10, 30, 20).rotate(Direction.EAST, dir, 0, 0);
    }
    
    private void drawNotGate(InstancePainter painter)
    {    	
    	
    	Graphics g = painter.getGraphics();
        Direction facing = (Direction) painter.getAttributeValue(StdAttr.FACING);
        AttributeOption size = (AttributeOption)painter.getAttributeValue(ATTR_SIZE);    	
        Location loc = painter.getLocation();
        int x = loc.getX();
        int y = loc.getY();
                    
        GraphicsUtil.switchToWidth(g, 2);
        int[] xp = new int[4];
        int[] yp = new int[4];
        int tx, ty, r;
        
        if(size.equals(SIZE_NARROW)) {
            xp[0] = x -  6; yp[0] = y;
            xp[1] = x - 19; yp[1] = y - 6;
            xp[2] = x - 19; yp[2] = y + 6;
            xp[3] = x -  6; yp[3] = y;
            tx = 6; ty = 3; r = 6;            
        } else {
            xp[0] = x - 10; yp[0] = y;
            xp[1] = x - 29; yp[1] = y - 7;
            xp[2] = x - 29; yp[2] = y + 7;
            xp[3] = x - 10; yp[3] = y;
            tx = 9; ty = 4; r = 9;            
        }
        
        Point[] pts = new Point[5];
        pts[0] = new Point(x, y);
        for(int i = 0; i < 4; i++)
        {
        	 pts[i + 1] = new Point(xp[i], yp[i]);
        }
        if(facing.equals(Direction.WEST))
        {
        	 GraphicsUtil.rotatePoints(pts, 180); 
        	 tx -= 10;
        }
        if(facing.equals(Direction.NORTH))
        {
        	 GraphicsUtil.rotatePoints(pts, 270); 
        	 tx -= 5;
        	 ty -= 5;
        }
        if(facing.equals(Direction.SOUTH))
        {
        	 GraphicsUtil.rotatePoints(pts, 90); 
        	 tx -= 5;
        	 ty += 5;
        }
        for(int i = 1; i < pts.length; i++)
        {
        	 xp[i - 1] = pts[i].x;
        	 yp[i - 1] = pts[i].y;
        }       
        
        g.drawPolyline(xp, yp, 4);
        g.drawOval(x - tx, y - ty, r, r);
    
    }

}
